#include <iostream>
#include <typeinfo>
#include <any>

class Any//一个能接收任意类型数据的容器
{
private:
    class Holder
    {
    public:
        //只要带了virtual 子类就会自动重写父类虚函数
        virtual ~Holder() {}//如果子类不重写父类析构 delete父类指针时就会调用父类析构 那么子类成员如果为自定义类型就不会析构
        virtual const std::type_info& getType() = 0;
        virtual Holder* clone() = 0;
    };

    template <class T>
    struct PlaceHolder : public Holder
    {
        T _val;

        PlaceHolder(const T& val)
        : _val(val)
        {}

        virtual const std::type_info& getType() { return typeid(T); }
        virtual Holder* clone() { return new PlaceHolder<T>(_val); }
    };

    Any& swap(Any& other)
    {
        std::swap(_content, other._content);
        return *this;
    }

    Holder* _content;
public:
    Any()
    : _content(nullptr)
    {}

    template <class T>
    Any(const T& val)//支持任意数据类型的构造 根据传入的参数类型自动生成相应的构造函数
    : _content(new PlaceHolder<T>(val))
    {}

    Any(const Any& other) { _content = other._content->clone(); }

    ~Any(){ delete _content; }

    template <class T>
    Any& operator=(const T& val)
    {
        Any(val).swap(*this);
        return *this;
    }

    Any& operator=(const Any& other)
    {
        Any(other).swap(*this);
        return *this;
    }

    template <class T>
    T* get() 
    {
        if (typeid(T) != _content->getType())//调用函数时传递的模板参数类型必须与存放的数据类型一致
            return nullptr;
        return &(((PlaceHolder<T>*)_content)->_val); 
    }
};

struct Test
{
    Test() { std::cout << "构造" << std::endl; }
    Test(const Test& t) { std::cout << "拷贝" << std::endl; }
    ~Test() { std::cout << "析构" << std::endl; }
};

int main()
{
    // Any a;
    // a = 10;
    // std::cout << *(a.get<int>()) << std::endl;
    // a = std::string("hello");
    // std::cout << *(a.get<std::string>()) << std::endl;
    // a = Test();

    //C++17 any
    std::any a;
    a = 10;
    std::cout << *(std::any_cast<int>(&a)) << std::endl;
    a = std::string("hello");
    std::cout << *(std::any_cast<std::string>(&a)) << std::endl;

    return 0;
}3